import {AxiosRequestConfig, AxiosPromise, AxiosResponse} from "../types/index";
import { parseHeaders } from "../helpers/headers";

import { createError } from "../helpers/error";
import CancelToken from "../cancel/CancelToken";
import {isUrlSameOrigin} from "../helpers/url";
import cookie from "../helpers/cookie";
import {isFormData} from "../helpers/util";

export default function xhr(config: AxiosRequestConfig): AxiosPromise{

    return new Promise((resolve, reject) => {


        const {data = null, url, method="GET", headers, responseType, timeout, CancelToken,
            withCredentials,
            xsrfHeaderName,
            xsrfCookieName,
            onDownloadProgress,
            onUploadProgress,
            auth,
            validateStatus
        } = config

        const request = new XMLHttpRequest()

        request.open(method.toUpperCase(), url!, true)

        configureRequest()

        addEvents()

        processHeaders()

        processCancel()

        request.send(data)



        function configureRequest(): void{
            if(responseType){
                request.responseType = responseType
            }

            // 超时
            if(timeout){
                request.timeout = timeout
            }

            // 跨域
            if(withCredentials){
                request.withCredentials = withCredentials
            }
        }

        function addEvents(): void{
            request.onreadystatechange = function  handleLoad(){
                if(request.readyState !== 4){
                    return
                }

                if(request.status === 0 ){
                    return
                }

                const responseHeaders = parseHeaders(request.getAllResponseHeaders())
                const responseData =  responseType !== 'text' ? request.response : request.responseText

                const response: AxiosResponse = {
                    data: responseData,
                    status: request.status,
                    statusText: request.statusText,
                    headers: responseHeaders,
                    config,
                    request
                }
                handlerResponse(response)
            }

            request.ontimeout = function handlerTimeout(){
                reject(createError(`Timeout of ${timeout}`, config, 'ECONNABORTED', request))
            }

            if(onDownloadProgress){
                request.onprogress = onDownloadProgress
            }

            if(onUploadProgress){
                request.upload.onprogress = onUploadProgress
            }

            request.onerror = function handlerError(){
                reject(createError('network error', config, null, request))
            }
        }

        function processHeaders(): void{
            if(isFormData(data)){
                delete headers['Content-type']
            }

            if((withCredentials || isUrlSameOrigin(url!)) && xsrfCookieName){
                const xsrfValue = cookie.read(xsrfCookieName)
                if(xsrfValue && xsrfHeaderName){
                    headers[xsrfHeaderName] = xsrfValue
                }
            }

            if(auth){
                headers['Authorization'] = 'Basic' + btoa(auth.username + ":"+ auth.password)
            }

            Object.keys(headers).forEach(name=>{
                if(data === null && name.toLowerCase() === 'content-type'){
                    delete headers[name]
                }else {
                    request.setRequestHeader(name, headers[name])
                }
            })
        }

        function processCancel(): void{
            if(CancelToken){
                CancelToken.promise.then(reason =>{
                    request.abort()
                    reject(reason)
                })
            }
        }

        function handlerResponse(res: AxiosResponse): void{
            if(!validateStatus || validateStatus(res.status)){
                resolve(res)
            }else{
                reject(createError(`Request failed status code $res.status}`, config, null, request))
            }
        }

    })
}